<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
import { useElementStyle, useElementPosition } from '@/hooks/useElement';
import confetti from 'canvas-confetti';
import { filterData, selectCard } from '@/utils';
import { rgba } from '@/utils/color';
import { Scene, PerspectiveCamera, Object3D, Vector3 } from 'three';
import { CSS3DRenderer, CSS3DObject } from 'three-css3d';
import { TrackballControls } from 'three/examples/jsm/controls/TrackballControls.js';
import * as TWEEN from '@tweenjs/tween.js';
import useStore from '@/store';
import { storeToRefs } from 'pinia';
import { useRouter } from 'vue-router';
import { useToast } from 'vue-toast-notification';

const toast = useToast();
const router = useRouter();
const personConfig = useStore().personConfig;
const globalConfig = useStore().globalConfig;
const prizeConfig = useStore().prizeConfig;

const {
  getAllPersonList: allPersonList,
  getNotPersonList: notPersonList,
  getNotThisPrizePersonList: notThisPrizePersonList,
} = storeToRefs(personConfig);
const { getCurrentPrize: currentPrize } = storeToRefs(prizeConfig);
const {
  getTopTitle: topTitle,
  getCardColor: cardColor,
  getPatterColor: patternColor,
  getPatternList: patternList,
  getTextColor: textColor,
  getLuckyColor: luckyColor,
  getCardSize: cardSize,
  getTextSize: textSize,
  getRowCount: rowCount,
} = storeToRefs(globalConfig);
const tableData = ref([]);
const currentStatus = ref(0); // 0为初始状态， 1为抽奖准备状态，2为抽奖中状态，3为抽奖结束状态
const ballRotationY = ref(0);
const sphereRef = ref();
// const LuckyViewRef= ref()
const canOperate = ref(true);
const cameraZ = ref(3000);

const scene = ref();
const camera = ref();
const renderer = ref();
const controls = ref();
const objects = ref([]);

const targets = {
  grid: [],
  helix: [],
  table: [],
  sphere: [],
};

const luckyTargets = ref([]);
const luckyCardList = ref([]);
let luckyCount = ref(10);
const personPool = ref([]);

const intervalTimer = ref(null);

// 填充数据，填满七行
function initTableData() {
  personConfig.setDefaultPersonList();
  if (allPersonList.value.length <= 0) {
    return;
  }
  const totalCount = rowCount.value * 7;
  const orginPersonData = JSON.parse(JSON.stringify(allPersonList.value));
  const orginPersonLength = orginPersonData.length;
  if (orginPersonLength < totalCount) {
    const repeatCount = Math.ceil(totalCount / orginPersonLength);
    // 复制数据
    for (let i = 0; i < repeatCount; i++) {
      tableData.value = tableData.value.concat(
        JSON.parse(JSON.stringify(orginPersonData)),
      );
    }
  } else {
    tableData.value = orginPersonData.slice(0, totalCount);
  }
  tableData.value = filterData(
    tableData.value.slice(0, totalCount),
    rowCount.value,
  );
}
const init = () => {
  const felidView = 40;
  const width = window.innerWidth;
  const height = window.innerHeight;
  const aspect = width / height;
  const nearPlane = 1;
  const farPlane = 10000;
  const WebGLoutput = sphereRef.value;

  scene.value = new Scene();
  camera.value = new PerspectiveCamera(felidView, aspect, nearPlane, farPlane);
  camera.value.position.z = cameraZ.value;
  renderer.value = new CSS3DRenderer();
  renderer.value.setSize(width, height * 0.9);
  renderer.value.domElement.style.position = 'absolute';
  // 垂直居中
  renderer.value.domElement.style.top = '50%';
  renderer.value.domElement.style.left = '50%';
  renderer.value.domElement.style.transform = 'translate(-50%, -50%)';
  WebGLoutput.appendChild(renderer.value.domElement);

  controls.value = new TrackballControls(
    camera.value,
    renderer.value.domElement,
  );
  controls.value.rotateSpeed = 1;
  controls.value.staticMoving = true;
  controls.value.minDistance = 500;
  controls.value.maxDistance = 6000;
  controls.value.enabled = false; // 设置不能操作
  controls.value.addEventListener('change', render);

  const tableLen = tableData.value.length;
  for (let i = 0; i < tableLen; i++) {
    let element = document.createElement('div');
    element.className = 'element-card';

    // 头像
    const avatar = document.createElement('img');
    avatar.className = 'card-avatar';
    avatar.src = tableData.value[i].avatar || 'default-avatar.jpg'; // 设置图片路径，使用默认头像作为后备
    avatar.alt = '生活照'; // 图片的替代文字
    element.appendChild(avatar);

    // 名字
    const pname = document.createElement('div');
    pname.className = 'card-name';
    pname.textContent = tableData.value[i].name;
    element.appendChild(pname);

    element = useElementStyle(
      element,
      tableData.value[i],
      i,
      patternList.value,
      patternColor.value,
      cardColor.value,
      cardSize.value,
      textSize.value,
    );
    const object = new CSS3DObject(element);
    object.position.x = Math.random() * 4000 - 2000;
    object.position.y = Math.random() * 4000 - 2000;
    object.position.z = Math.random() * 4000 - 2000;
    scene.value.add(object);

    objects.value.push(object);
  }

  createTableVertices();
  createSphereVertices();
  createHelixVertices();

  function createTableVertices() {
    const tableLen = tableData.value.length;

    for (let i = 0; i < tableLen; i++) {
      const object = new Object3D();

      object.position.x =
        tableData.value[i].x * (cardSize.value.width + 40) -
        rowCount.value * 90;
      object.position.y =
        -tableData.value[i].y * (cardSize.value.height + 20) + 1000;
      object.position.z = 0;

      targets.table.push(object);
    }
  }

  function createSphereVertices() {
    const objLength = objects.value.length;
    const vector = new Vector3();

    // 球体半径
    const radius = 900;

    // 计算常量，避免在循环中重复计算
    const PI = Math.PI;
    const sqrtPI = Math.sqrt(objLength * PI);

    // 优化的分布方式，提前计算phi和theta的值
    for (let i = 0; i < objLength; ++i) {
      // 计算phi和theta的值
      const phi = Math.acos(-1 + (2 * i) / objLength); // 计算纬度
      const theta = sqrtPI * phi; // 计算经度，sqrtPI是常量

      const object = new Object3D();

      // 球面坐标转化为3D位置
      const sinPhi = Math.sin(phi);
      object.position.x = radius * Math.cos(theta) * sinPhi;
      object.position.y = radius * Math.sin(theta) * sinPhi;
      object.position.z = -radius * Math.cos(phi);

      // 计算物体的朝向
      vector
        .set(object.position.x, object.position.y, object.position.z)
        .multiplyScalar(2);
      object.lookAt(vector);

      targets.sphere.push(object);
    }
  }

  function createHelixVertices() {
    let i = 0;
    const vector = new Vector3();
    const objLength = objects.value.length;
    for (; i < objLength; ++i) {
      let phi = i * 0.213 + Math.PI;

      const object = new Object3D();

      object.position.x = 800 * Math.sin(phi);
      object.position.y = -(i * 8) + 450;
      object.position.z = 800 * Math.cos(phi + Math.PI);

      object.scale.set(1.1, 1.1, 1.1);

      vector.x = object.position.x * 2;
      vector.y = object.position.y;
      vector.z = object.position.z * 2;

      object.lookAt(vector);

      targets.helix.push(object);
    }
  }
  window.addEventListener('resize', onWindowResize, false);
  transform(targets.table, 1000);
  render();
};

const transform = (targets, duration) => {
  TWEEN.removeAll();
  if (intervalTimer.value) {
    clearInterval(intervalTimer.value);
    intervalTimer.value = null;
    randomBallData('sphere');
  }

  return new Promise((resolve) => {
    const objLength = objects.value.length;
    for (let i = 0; i < objLength; ++i) {
      let object = objects.value[i];
      let target = targets[i];
      new TWEEN.Tween(object.position)
        .to(
          { x: target.position.x, y: target.position.y, z: target.position.z },
          Math.random() * duration + duration,
        )
        .easing(TWEEN.Easing.Exponential.InOut)
        .start();

      new TWEEN.Tween(object.rotation)
        .to(
          { x: target.rotation.x, y: target.rotation.y, z: target.rotation.z },
          Math.random() * duration + duration,
        )
        .easing(TWEEN.Easing.Exponential.InOut)
        .start()
        .onComplete(() => {
          if (luckyCardList.value.length) {
            luckyCardList.value.forEach((cardIndex) => {
              const item = objects.value[cardIndex];
              useElementStyle(
                item.element,
                {},
                i,
                patternList.value,
                patternColor.value,
                cardColor.value,
                cardSize.value,
                textSize.value,
                'sphere',
              );
            });
          }
          luckyTargets.value = [];
          luckyCardList.value = [];

          canOperate.value = true;
        });
    }

    // 这个补间用来在位置与旋转补间同步执行，通过onUpdate在每次更新数据后渲染scene和camera
    new TWEEN.Tween({})
      .to({}, duration * 2)
      .onUpdate(render)
      .start()
      .onComplete(() => {
        canOperate.value = true;
        resolve('');
      });
  });
};
function onWindowResize() {
  camera.value.aspect = window.innerWidth / window.innerHeight;
  camera.value.updateProjectionMatrix();

  renderer.value.setSize(window.innerWidth, window.innerHeight);
  render();
}

/**
 * [animation update all tween && controls]
 */
function animation() {
  TWEEN.update();
  controls.value.update();
  // 设置自动旋转
  // 设置相机位置
  requestAnimationFrame(animation);
}
// 旋转的动画
function rollBall(rotateY, duration) {
  TWEEN.removeAll();

  return new Promise((resolve) => {
    scene.value.rotation.y = 0;
    ballRotationY.value = Math.PI * rotateY * 1000;
    const rotateObj = new TWEEN.Tween(scene.value.rotation)
      .to(
        {
          x: 0,
          y: ballRotationY.value,
          z: 0,
        },
        duration * 300, // 可根据需要调整持续时间
      )
      .onUpdate(render)
      .start()
      .onStop(() => {
        resolve('');
      })
      .onComplete(() => {
        resolve('');
      });
  });
}

// 将视野转回正面
function resetCamera() {
  new TWEEN.Tween(camera.value.position)
    .to(
      {
        x: 0,
        y: 0,
        z: 3000,
      },
      1000,
    )
    .onUpdate(render)
    .start()
    .onComplete(() => {
      new TWEEN.Tween(camera.value.rotation)
        .to(
          {
            x: 0,
            y: 0,
            z: 0,
          },
          1000,
        )
        .onUpdate(render)
        .start()
        .onComplete(() => {
          canOperate.value = true;
          // camera.value.lookAt(scene.value.position)
          camera.value.position.y = 0;
          camera.value.position.x = 0;
          camera.value.position.z = 3000;
          camera.value.rotation.x = 0;
          camera.value.rotation.y = 0;
          camera.value.rotation.z = -0;
          controls.value.reset();
        });
    });
}

function render() {
  renderer.value.render(scene.value, camera.value);
}
const enterLottery = async () => {
  if (!canOperate.value) {
    return;
  }
  if (!intervalTimer.value) {
    randomBallData();
  }
  if (patternList.value.length) {
    for (let i = 0; i < patternList.value.length; i++) {
      if (i < rowCount.value * 7) {
        objects.value[patternList.value[i] - 1].element.style.backgroundColor =
          rgba(cardColor.value, Math.random() * 0.5 + 0.25);
      }
    }
  }
  canOperate.value = false;
  await transform(targets.sphere, 1000);
  currentStatus.value = 1;
  rollBall(0.1, 2000);
};
// 开始抽奖
const startLottery = () => {
  if (!canOperate.value) {
    return;
  }
  // 验证是否已抽完全部奖项
  if (currentPrize.value.isUsed || !currentPrize.value) {
    toast.open({
      message: '抽奖抽完了',
      type: 'warning',
      position: 'top-right',
      duration: 10000,
    });

    return;
  }
  personPool.value = currentPrize.value.isAll
    ? notThisPrizePersonList.value
    : notPersonList.value;
  // 验证抽奖人数是否还够
  if (
    personPool.value.length <
    currentPrize.value.count - currentPrize.value.isUsedCount
  ) {
    toast.open({
      message: '抽奖人数不够',
      type: 'warning',
      position: 'top-right',
      duration: 10000,
    });

    return;
  }
  luckyCount.value = 10;
  // 自定义抽奖个数

  let leftover = currentPrize.value.count - currentPrize.value.isUsedCount;
  const customCount = currentPrize.value.separateCount;
  if (customCount && customCount.enable && customCount.countList.length > 0) {
    for (let i = 0; i < customCount.countList.length; i++) {
      if (
        customCount.countList[i].isUsedCount < customCount.countList[i].count
      ) {
        leftover =
          customCount.countList[i].count - customCount.countList[i].isUsedCount;
        break;
      }
    }
  }
  leftover < luckyCount.value ? (luckyCount.value = leftover) : luckyCount;
  for (let i = 0; i < luckyCount.value; i++) {
    if (personPool.value.length > 0) {
      const randomIndex = Math.round(
        Math.random() * (personPool.value.length - 1),
      );
      luckyTargets.value.push(personPool.value[randomIndex]);
      personPool.value.splice(randomIndex, 1);
    }
  }
  toast.open({
    message: `现在抽取${currentPrize.value.name} ${leftover}人`,
    type: 'default',
    position: 'top-right',
    duration: 8000,
  });
  currentStatus.value = 2;
  rollBall(1, 3000);
};

const stopLottery = async () => {
  if (!canOperate.value) {
    return;
  }
  clearInterval(intervalTimer.value);
  intervalTimer.value = null;
  canOperate.value = false;
  rollBall(0, 1);

  const windowSize = { width: window.innerWidth, height: window.innerHeight };
  luckyTargets.value.forEach((person, index) => {
    let cardIndex = selectCard(
      luckyCardList.value,
      tableData.value.length,
      person.id,
    );
    luckyCardList.value.push(cardIndex);
    let item = objects.value[cardIndex];
    const { xTable, yTable } = useElementPosition(
      item,
      rowCount.value,
      { width: cardSize.value.width * 2, height: cardSize.value.height * 2 },
      windowSize,
      index,
    );
    new TWEEN.Tween(item.position)
      .to(
        {
          x: xTable,
          y: yTable,
          z: 1000,
        },
        1200,
      )
      .easing(TWEEN.Easing.Exponential.InOut)
      .onStart(() => {
        item.element = useElementStyle(
          item.element,
          person,
          cardIndex,
          patternList.value,
          patternColor.value,
          luckyColor.value,
          {
            width: cardSize.value.width * 2,
            height: cardSize.value.height * 2,
          },
          textSize.value * 2,
          'lucky',
        );
      })
      .start()
      .onComplete(() => {
        canOperate.value = true;
        currentStatus.value = 3;
      });
    new TWEEN.Tween(item.rotation)
      .to(
        {
          x: 0,
          y: 0,
          z: 0,
        },
        900,
      )
      .easing(TWEEN.Easing.Exponential.InOut)
      .start()
      .onComplete(() => {
        confettiFire();
        resetCamera();
      });
  });
};
// 继续
const continueLottery = async () => {
  if (!canOperate.value) {
    return;
  }

  const customCount = currentPrize.value.separateCount;
  if (customCount && customCount.enable && customCount.countList.length > 0) {
    for (let i = 0; i < customCount.countList.length; i++) {
      if (
        customCount.countList[i].isUsedCount < customCount.countList[i].count
      ) {
        customCount.countList[i].isUsedCount += luckyCount.value;
        break;
      }
    }
  }
  currentPrize.value.isUsedCount += luckyCount.value;
  luckyCount.value = 0;
  if (currentPrize.value.isUsedCount >= currentPrize.value.count) {
    currentPrize.value.isUsed = true;
    currentPrize.value.isUsedCount = currentPrize.value.count;
  }
  personConfig.addAlreadyPersonList(luckyTargets.value, currentPrize.value);
  prizeConfig.updatePrizeConfig(currentPrize.value);
  await enterLottery();
};
const quitLottery = () => {
  enterLottery();
  currentStatus.value = 0;
};
// 庆祝动画
const confettiFire = () => {
  const duration = 3 * 1000;
  const end = Date.now() + duration;
  (function frame() {
    // launch a few confetti from the left edge
    confetti({
      particleCount: 2,
      angle: 60,
      spread: 55,
      origin: { x: 0 },
    });
    // and launch a few from the right edge
    confetti({
      particleCount: 2,
      angle: 120,
      spread: 55,
      origin: { x: 1 },
    });

    // keep going until we are out of time
    if (Date.now() < end) {
      requestAnimationFrame(frame);
    }
  })();
  centerFire(0.25, {
    spread: 26,
    startVelocity: 55,
  });
  centerFire(0.2, {
    spread: 60,
  });
  centerFire(0.35, {
    spread: 100,
    decay: 0.91,
    scalar: 0.8,
  });
  centerFire(0.1, {
    spread: 120,
    startVelocity: 25,
    decay: 0.92,
    scalar: 1.2,
  });
  centerFire(0.1, {
    spread: 120,
    startVelocity: 45,
  });
};
const centerFire = (particleRatio, opts) => {
  const count = 200;
  confetti({
    origin: { y: 0.7 },
    ...opts,
    particleCount: Math.floor(count * particleRatio),
  });
};

const setDefaultPersonList = () => {
  personConfig.setDefaultPersonList();
  // 刷新页面
  window.location.reload();
};
// 随机替换数据 'default' | 'lucky' | 'sphere
const randomBallData = (mod = 'default') => {
  // 两秒执行一次
  intervalTimer.value = setInterval(() => {
    // 产生随机数数组
    const indexLength = 4;
    const cardRandomIndexArr = [];
    const personRandomIndexArr = [];
    for (let i = 0; i < indexLength; i++) {
      cardRandomIndexArr.push(
        Math.round(Math.random() * (tableData.value.length - 1)),
      );
      personRandomIndexArr.push(
        Math.round(Math.random() * (allPersonList.value.length - 1)),
      );
    }
    for (let i = 0; i < cardRandomIndexArr.length; i++) {
      if (!objects.value[cardRandomIndexArr[i]]) {
        continue;
      }
      objects.value[cardRandomIndexArr[i]].element = useElementStyle(
        objects.value[cardRandomIndexArr[i]].element,
        allPersonList.value[personRandomIndexArr[i]],
        cardRandomIndexArr[i],
        patternList.value,
        patternColor.value,
        cardColor.value,
        { width: cardSize.value.width, height: cardSize.value.height },
        textSize.value,
        mod,
      );
    }
  }, 200);
};
// 监听键盘
const listenKeyboard = () => {
  window.addEventListener('keydown', (e) => {
    if ((e.keyCode !== 32 || e.keyCode !== 27) && !canOperate.value) {
      return;
    }
    if (e.keyCode === 27 && currentStatus.value === 3) {
      quitLottery();
    }
    if (e.keyCode !== 32) {
      return;
    }
    switch (currentStatus.value) {
      case 0:
        enterLottery();
        break;
      case 1:
        startLottery();
        break;
      case 2:
        stopLottery();
        break;
      case 3:
        continueLottery();
        break;
      default:
        break;
    }
  });
};
onMounted(() => {
  initTableData();
  init();
  animation();
  sphereRef.value.style.color = `${textColor}`;
  randomBallData();
  listenKeyboard();
});
onUnmounted(() => {
  clearInterval(intervalTimer.value);
  intervalTimer.value = null;
  window.removeEventListener('keydown', listenKeyboard);
});
</script>

<template>
  <div id="sphere" ref="sphereRef" class="3dSphere"></div>
  <div id="sphere-button">
    <!-- 进入抽奖按钮 -->
    <button
      class="btn-end"
      @click="enterLottery"
      v-if="currentStatus === 0 && tableData.length > 0"
    >
      进入抽奖
    </button>

    <!-- 开始抽奖按钮 -->
    <div class="start" v-if="currentStatus === 1">
      <button class="btn-start" @click="startLottery">
        <strong>开始</strong>
        <!-- 星星动画 -->
        <div id="sphere-stars">
          <div id="stars"></div>
        </div>
        <!-- 按钮发光效果 -->
        <div id="glow">
          <div class="circle"></div>
          <div class="circle"></div>
        </div>
      </button>
    </div>

    <!-- 抽取幸运儿按钮 -->
    <button
      class="btn-end btn glass btn-lg"
      @click="stopLottery"
      v-if="currentStatus === 2"
    >
      抽取幸运儿
    </button>

    <!-- 继续和取消按钮 -->
    <div v-if="currentStatus === 3" class="continue-buttons">
      <!-- 继续按钮 -->
      <div class="start">
        <button class="btn-start" @click="continueLottery">
          <strong>继续！</strong>
          <!-- 星星动画 -->
          <div id="sphere-stars">
            <div id="stars"></div>
          </div>
          <!-- 按钮发光效果 -->
          <div id="glow">
            <div class="circle"></div>
            <div class="circle"></div>
          </div>
        </button>
      </div>

      <!-- 取消按钮 -->
      <div class="start">
        <button class="btn-cancel" @click="quitLottery">
          <strong>取消</strong>
          <!-- 星星动画 -->
          <div id="sphere-stars">
            <div id="stars"></div>
          </div>
          <!-- 按钮发光效果 -->
          <div id="glow">
            <div class="circle"></div>
            <div class="circle"></div>
          </div>
        </button>
      </div>
    </div>
  </div>
</template>

<style scoped lang="scss">
@import './index.scss';
</style>
